package lumis.report.interfacegeneration;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;

import lumis.portal.PortalException;
import lumis.portal.authentication.SessionConfig;
import lumis.portal.manager.ManagerFactory;
import lumis.portal.monitor.IAggregationType;
import lumis.portal.service.ServiceConfig;
import lumis.portal.transaction.PortalTransactionFactory;
import lumis.report.IJRDataSourceProvider;
import lumis.report.util.ReportUtil;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRMapCollectionDataSource;

/**
 * Data source provider used by the Interface Generation report.
 * 
 * @version $Revision: 13092 $ $Date: 2011-05-28 18:19:06 -0300 (Sat, 28 May 2011) $
 * @since 6.0.0
 */
public class InterfaceGenerationJRDataSourceProvider implements IJRDataSourceProvider
{	
	/**
	 * Reads the data from persistence.
	 * @param startDate the start of the period to be read from. If null
	 * 30 days previous to the current day will be used. The time part of the
	 * date is ignored.
	 * @param endDate the end of the period to be read from. If null the current
	 * day will be used. The time part of the date is ignored.
	 * @return the data rows.
	 * @since 6.0.0
	 */
	@SuppressWarnings("unchecked")
	private static List<Map<String,Object>> readData(Date startDate, Date endDate) throws JRException
	{
		//if start date and end date was not specified, it sets default values ( 30 days before as start and the actual date as end )
		Calendar calendar = Calendar.getInstance();
		if (startDate == null)
		{
			calendar.add(Calendar.DATE, -30);
			startDate = calendar.getTime();
		}
		if (endDate == null)
		{
			endDate = new Date (System.currentTimeMillis());
		}
		//truncate endDate and startDate
		startDate = ReportUtil.truncateDateTime(startDate);
		calendar.setTime(endDate);
		calendar.add(Calendar.DATE, 1);
		endDate = ReportUtil.truncateDateTime(calendar.getTime());
		//adds 1 day to endDate, to include the end day
		endDate = new Date (endDate.getTime() - 1);	
		
		EntityManager entityManager = ManagerFactory.getEntityManager();
		
		String query = 
			"SELECT " + 
			"aliasKey.channel, " +
			"aliasKey.pageName, " +
			"aliasKey.serviceInterface, " +
			"aliasKey.xslName, " +
			"aliasKey.averageGen " +
			
			"FROM (SELECT "+
			
			"channelAggregation.value as channel, " +
			"pageNameAggregation.value as pageName, " +
			"serviceInterfaceAggregation.value as serviceInterface, " +
			"xslAggregation.value as xslName, " +
			"SUM(durationMeasure.sumOfValues)/SUM(durationMeasure.hits) as averageGen " +
			
			"FROM lum_MonEventData eventData " +
			
			"INNER JOIN lum_MonEventDataMeasure durationMeasure ON durationMeasure.eventDataId = eventData.id INNER JOIN lum_MonMeasureType durationMeasureType " +
			"ON durationMeasure.measureTypeId = durationMeasureType.id " +
			"AND durationMeasureType.measureTypeKey = 'lumis.portal.monitor.mt.duration.ms' " +
			 
			"INNER JOIN lum_MonEventDataAggregation pageNameAggregation " +
			"ON pageNameAggregation.eventDataId = eventData.id " +
			"INNER JOIN lum_MonAggregationType pageNameAggregationType " +
			"ON pageNameAggregation.aggregationTypeId = pageNameAggregationType.id  " +
			"AND pageNameAggregationType.aggregationTypeKey = '" + IAggregationType.AGGREGATION_TYPE_PAGE_NAME + "' " +
			 
			"INNER JOIN lum_MonEventDataAggregation channelAggregation " +
			"ON channelAggregation.eventDataId = eventData.id " +
			"INNER JOIN lum_MonAggregationType channelAggregationType " +
			"ON channelAggregation.aggregationTypeId = channelAggregationType.id " +
			"AND channelAggregationType.aggregationTypeKey = '" + IAggregationType.AGGREGATION_TYPE_CHANNEL_PATH + "' " +
			 
			"INNER JOIN lum_MonEventDataAggregation serviceInterfaceAggregation " +
			"ON serviceInterfaceAggregation.eventDataId = eventData.id " +
			"INNER JOIN lum_MonAggregationType serviceInterfaceAggregationType " +
			"ON serviceInterfaceAggregation.aggregationTypeId = serviceInterfaceAggregationType.id " +
			"AND serviceInterfaceAggregationType.aggregationTypeKey = '" + IAggregationType.AGGREGATION_TYPE_SERVICE_INTERFACE_ID + "' " +
			 
			"INNER JOIN lum_MonEventDataAggregation xslAggregation " +
			"ON xslAggregation.eventDataId = eventData.id " +
			"INNER JOIN lum_MonAggregationType xslAggregationType " +
			"ON xslAggregation.aggregationTypeId = xslAggregationType.id " +
			"AND xslAggregationType.aggregationTypeKey = '" + IAggregationType.AGGREGATION_TYPE_SERVICE_INTERFACE_INSTANCE_XSL_FILE + "' " + 
			"WHERE eventData.startOfPeriod >= ? AND eventData.endOfPeriod <= ? " + 
			"group by channelAggregation.value, pageNameAggregation.value, serviceInterfaceAggregation.value, xslAggregation.value) aliasKey "+
			"order by averageGen desc";
		List<Object[]> interfacesGenerated = entityManager.createNativeQuery(query)
				.setParameter(1, startDate)
				.setParameter(2, endDate)
				.getResultList();
				
		List<Map<String, Object>> result = new ArrayList<Map<String,Object>>();
		for(Object[] interfaceGenerated : interfacesGenerated)
		{
			//sets the parameters to be sent to Jasper
			Map<String, Object> parameters = new HashMap<String, Object>();
			parameters.put("channel", interfaceGenerated[0]);
			parameters.put("pageName", interfaceGenerated[1]);
			parameters.put("serviceInterface", interfaceGenerated[2]);
			String serviceName = null;
			try
			{
				//gets the serviceInterfaceId to find out the service name
				String interfaceId = (String)interfaceGenerated[2];
				String serviceId = interfaceId.substring(0, interfaceId.lastIndexOf('.'));
				ServiceConfig serviceConfig = ManagerFactory.getServiceManager().get(SessionConfig.getCurrentSessionConfig(), serviceId, PortalTransactionFactory.getCurrentTransaction());
				serviceName = serviceConfig.getName();
				serviceName = ManagerFactory.getLocalizationManager().localize(serviceName, serviceConfig.getResource());
			}
			catch (Exception e)
			{
				throw new JRException(e.getMessage(),e);
			}
			parameters.put("serviceInterfaceInstanceName", serviceName);
			parameters.put("xslName", interfaceGenerated[3]);
			parameters.put("averageGen", interfaceGenerated[4]);
			result.add(parameters);			
		}		
		return result;
	}
	
	/**
	 * Returns the data source to be used in the table of the report.
	 * @param startDate the start of the period to be read from. If null
	 * 30 days previous to the current day will be used. The time part of the
	 * date is ignored.
	 * @param endDate the end of the period to be read from. If null the current
	 * day will be used. The time part of the date is ignored.
	 * @return the data source.
	 * @since 6.0.0
	 */
	public static JRDataSource getDataForTable(final Date startDate, final Date endDate) throws JRException
	{
		return new JRMapCollectionDataSource(readData(startDate, endDate));
	}
	
	public JRDataSource create(JasperReport jasperreport, Map<String, Object> parameters) throws JRException, PortalException
	{
		final Date startDate = (Date) parameters.get("startDate");
		final Date endDate = (Date) parameters.get("endDate");
		
		return getDataForTable(startDate, endDate);
	}

	public void dispose(JRDataSource jrDataSource) throws JRException, PortalException
	{
		
	}
}
